#include "src/Helpers/_CPlugin_Helper.h"
#ifdef USES_C009

# include "src/DataTypes/NodeTypeID.h"
# include "src/Helpers/StringProvider.h"
# include "src/CustomBuild/ESPEasy_buildinfo.h"

// #######################################################################################################
// ########################### Controller Plugin 009: FHEM HTTP ##########################################
// #######################################################################################################

/*******************************************************************************
 * Copyright 2016-2017 dev0
 * Contact: https://forum.fhem.de/index.php?action=profile;u=7465
 *          https://github.com/ddtlabs/
 *
 * Release notes:
   - v1.0
   - changed switch and dimmer setreading cmds
   - v1.01
   - added json content to http requests
   - v1.02
   - some optimizations as requested by mvdbro
   - fixed JSON TaskDeviceValueDecimals handling
   - ArduinoJson Library v5.6.4 required (as used by stable R120)
   - parse for HTTP errors 400, 401
   - moved on/off translation for Sensor_VType::SENSOR_TYPE_SWITCH/DIMMER to FHEM module
   - v1.03
   - changed http request from GET to POST (RFC conform)
   - removed obsolete http get url code
   - v1.04
   - added build options and node_type_id to JSON/device
 ******************************************************************************/

# define CPLUGIN_009
# define CPLUGIN_ID_009         9
# define CPLUGIN_NAME_009       "FHEM HTTP"

bool CPlugin_009(CPlugin::Function function, struct EventStruct *event, String& string)
{
  bool success = false;

  switch (function)
  {
    case CPlugin::Function::CPLUGIN_PROTOCOL_ADD:
    {
      ProtocolStruct& proto = getProtocolStruct(event->idx); //      = CPLUGIN_ID_009;
      proto.usesMQTT     = false;
      proto.usesTemplate = false;
      proto.usesAccount  = true;
      proto.usesPassword = true;
      proto.usesExtCreds = true;
      proto.usesID       = false;
      proto.defaultPort  = 8383;
      # if FEATURE_HTTP_TLS
      proto.usesTLS = true;
      # endif // if FEATURE_HTTP_TLS
      break;
    }

    case CPlugin::Function::CPLUGIN_GET_DEVICENAME:
    {
      string = F(CPLUGIN_NAME_009);
      break;
    }

    case CPlugin::Function::CPLUGIN_INIT:
    {
      success = init_c009_delay_queue(event->ControllerIndex);
      break;
    }

    case CPlugin::Function::CPLUGIN_EXIT:
    {
      exit_c009_delay_queue();
      break;
    }

    case CPlugin::Function::CPLUGIN_PROTOCOL_SEND:
    {
      if (C009_DelayHandler != nullptr) {
        if (C009_DelayHandler->queueFull(event->ControllerIndex)) {
          break;
        }

        constexpr unsigned size = sizeof(C009_queue_element);
        void *ptr               = special_calloc(1, size);

        if (ptr != nullptr) {
          std::unique_ptr<C009_queue_element> element(new (ptr) C009_queue_element(event));
          success = C009_DelayHandler->addToQueue(std::move(element));
        }
        Scheduler.scheduleNextDelayQueue(SchedulerIntervalTimer_e::TIMER_C009_DELAY_QUEUE, C009_DelayHandler->getNextScheduleTime());
      }
      break;
    }

    case CPlugin::Function::CPLUGIN_FLUSH:
    {
      process_c009_delay_queue();
      delay(0);
      break;
    }

    default:
      break;
  }
  return success;
}

/*********************************************************************************************\
* FHEM HTTP request
\*********************************************************************************************/

// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
bool do_process_c009_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
  const C009_queue_element& element = static_cast<const C009_queue_element&>(element_base);
// *INDENT-ON*
String jsonString;

// Make an educated guess on the actual length, based on earlier requests.
static size_t expectedJsonLength = 100;
{
  // Reserve on the heap with most space
  if (!reserve_special(jsonString, expectedJsonLength)) {
    // Not enough free memory
    return false;
  }
}
{
  jsonString += '{';
  {
    jsonString += to_json_object_value(F("module"),  F("ESPEasy"));
    jsonString += ',';
    jsonString += to_json_object_value(F("version"), F("1.04"));

    // Create nested object "ESP" inside "data"
    jsonString += ',';
    jsonString += F("\"data\":{");
    {
      jsonString += F("\"ESP\":{");
      {
        // Create nested objects in "ESP":
        jsonString += to_json_object_value(F("name"), Settings.getName());
        jsonString += ',';
        jsonString += to_json_object_value(F("unit"), static_cast<int>(Settings.Unit));
        jsonString += ',';
        jsonString += to_json_object_value(F("version"), static_cast<int>(Settings.Version));
        jsonString += ',';
        jsonString += to_json_object_value(F("build"), static_cast<int>(Settings.Build));
        jsonString += ',';
        jsonString += to_json_object_value(F("build_notes"), F(BUILD_NOTES));
        jsonString += ',';
        jsonString += to_json_object_value(F("build_git"), getValue(LabelType::GIT_BUILD));
        jsonString += ',';
        jsonString += to_json_object_value(F("node_type_id"), static_cast<int>(NODE_TYPE_ID));
        jsonString += ',';
        jsonString += to_json_object_value(F("sleep"), static_cast<int>(Settings.deepSleep_wakeTime));

        // embed IP, important if there is NAT/PAT
        // char ipStr[20];
        // IPAddress ip = NetworkLocalIP();
        // sprintf_P(ipStr, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
        jsonString += ',';
        jsonString += to_json_object_value(F("ip"), formatIP(NetworkLocalIP()));
      }
      jsonString += '}'; // End "ESP"

      jsonString += ',';

      // Create nested object "SENSOR" json object inside "data"
      jsonString += F("\"SENSOR\":{");
      {
        // char itemNames[valueCount][2];
        for (uint8_t x = 0; x < element.valueCount; x++)
        {
          // Each sensor value get an own object (0..n)
          // sprintf(itemNames[x],"%d",x);
          if (x != 0) {
            jsonString += ',';
          }

          jsonString += '"';
          jsonString += x;
          jsonString += F("\":{");
          {
            jsonString += to_json_object_value(F("deviceName"), getTaskDeviceName(element._taskIndex));
            jsonString += ',';
            jsonString += to_json_object_value(F("valueName"), Cache.getTaskDeviceValueName(element._taskIndex, x));
            jsonString += ',';
            jsonString += to_json_object_value(F("type"), static_cast<int>(element.sensorType));
            jsonString += ',';
            jsonString += to_json_object_value(F("value"), element.txt[x]);
          }
          jsonString += '}'; // End "sensor value N"
        }
      }
      jsonString += '}';     // End "SENSOR"
    }
    jsonString += '}';       // End "data"
  }
  jsonString += '}';         // End JSON structure
}

if (expectedJsonLength < jsonString.length()) {
  expectedJsonLength = jsonString.length();
}

// addLog(LOG_LEVEL_INFO, F("C009 Test JSON:"));
// addLog(LOG_LEVEL_INFO, jsonString);

int httpCode = -1;
send_via_http(
  cpluginID,
  ControllerSettings,
  element._controller_idx,
  F("/ESPEasy"),
  F("POST"),
  EMPTY_STRING,
  jsonString,
  httpCode);
return (httpCode >= 100) && (httpCode < 300);
}

#endif // ifdef USES_C009
